using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.Rendering;

// [CreateAfter(typeof(AStarSystem))]
partial struct EnemyManager : ISystem {
	public NativeArray<AStarNode> nodes;

	public void OnCreate(ref SystemState state) {
		foreach (var aStarMap in SystemAPI.Query<RefRO<AStarMap>>()) {
			nodes = aStarMap.ValueRO.nodes;
		}

		World world = World.DefaultGameObjectInjectionWorld;
		var entityManager = world.EntityManager;

		// GameObject prefab = Resources.Load<GameObject>("Cube");
		GameObject prefab = Resources.Load<GameObject>("Ghost");
		var renderMeshArray = new RenderMeshArray(new Material[] { prefab.GetComponent<MeshRenderer>().sharedMaterial },
			new Mesh[] { prefab.GetComponent<MeshFilter>().sharedMesh });

		var desc = new RenderMeshDescription(
		   shadowCastingMode: ShadowCastingMode.Off,
		   receiveShadows: false);
		var prototype = entityManager.CreateEntity();

		RenderMeshUtility.AddComponents(
		   prototype,
		   entityManager,
		   desc,
		   renderMeshArray,
		   MaterialMeshInfo.FromRenderMeshArrayIndices(0, 0)
		);

		entityManager.AddComponentData(prototype, new LocalTransform());
		entityManager.AddComponentData(prototype, new Agent());
		entityManager.AddComponentData(prototype, new AStarAgent());
		entityManager.AddComponentData(prototype, new AnimState());
		entityManager.AddComponentData(prototype, new URPMaterialPropertyCurTime());
		entityManager.AddComponentData(prototype, new URPMaterialPropertyStartTime());
		entityManager.AddComponentData(prototype, new URPMaterialPropertyEndTime());
		entityManager.AddComponentData(prototype, new URPMaterialPropertyIsLoop());

		Agent agent = new Agent() {
			id = -1,
			position = new float2(0, 0),
			radius = 0.3f,
			weight = 0.5f,
			maxNeighbors = 8,
			neighborDist = 3,
			timeHorizon = 5,
			timeHorizonObst = 5,
			velocity = 1,
			prefVelocity = 1,
			targetPos = new float2(0, 0),
			maxSpeed = 1.0f
		};

		var ecb = new EntityCommandBuffer(Allocator.TempJob);
		var spawnJob = new SpawnJob {
			Prototype = prototype,
			Ecb = ecb.AsParallelWriter(),
			Agent = agent,
			Nodes = nodes,
		};
		var spawnHandle = spawnJob.Schedule(1000, 64);
		spawnHandle.Complete();

		ecb.Playback(entityManager);
		ecb.Dispose();

		entityManager.DestroyEntity(prototype);
	}

	public void OnUpdate(ref SystemState state) {

	}

	public void OnDestory(ref SystemState state) {

	}

	[BurstCompile]
	public struct SpawnJob : IJobParallelFor {
		public Entity Prototype;
		public EntityCommandBuffer.ParallelWriter Ecb;
		public Agent Agent;
		[ReadOnly] public NativeArray<AStarNode> Nodes;

		public void Execute(int index) {
			if (!Nodes[index].isWalkable) {
				return;
			}

			(int x, int y) = GetCoordinate(index);

			SetAgent(index, x, y);

			var e = Ecb.Instantiate(index, Prototype);
			Ecb.SetComponent(index, e, new LocalTransform { Position = new float3(x, 0.5f, y), Scale = 1, Rotation = quaternion.identity });
			Ecb.SetComponent(index, e, Agent);
			Ecb.SetComponent(index, e, new AStarAgent { myNodeIndex = index, nextNodeIndex = -1, count = 0 });
			Ecb.SetComponent(index, e, new AnimState { curState = AnimStateType.Idle, preState = AnimStateType.Idle });
			Ecb.SetComponent(index, e, new URPMaterialPropertyCurTime { Value = 0 });
			Ecb.SetComponent(index, e, new URPMaterialPropertyStartTime { Value = 0 });
			Ecb.SetComponent(index, e, new URPMaterialPropertyEndTime { Value = 1.34f });
			Ecb.SetComponent(index, e, new URPMaterialPropertyIsLoop { Value = 1 });
		}

		public void SetAgent(int index, int x, int y) {
			Agent.id++;
			float3 position = new float3(x, 0.5f, y);
			float2 v = math.normalizesafe(new float2(0, 0) - position.xz) * 1.0f;
			Agent.position = position.xz;
			Agent.velocity = v;
			Agent.prefVelocity = v;
			Agent.targetPos = new float2(0, 0);
		}

		private (int x, int y) GetCoordinate(int index) {
			int width = 2 * 49 + 1;
			int x = index % width - 49;
			int y = 49 - index / width;
			return (x, y);
		}
	}
}

